// 一.数组扁平化
const arr1 = [1, [2, [3, [4, 5]]], 6]

const res1 = arr1.flat(Infinity)

const flatten = arr => {
    return arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur) ? flatten(cur) : cur)
    }, [])
}
const res2 = flatten(arr1)

const res3 = []
const func = arr => {
    arr.forEach(e => {
        if (Array.isArray(e)) {
            func(e)
        } else {
            res3.push(e)
        }
    })
}
func(arr1)


// 二.数组去重
const arr2 = [1, 1, '1', 2, true, true, false, false, 'true', 'a', {}, {}]

const res4 = Array.from(new Set(arr2))

const unique1 = arr => {
    let len = arr.length
    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            if (arr[i] === arr[j]) {
                arr.splice(j, 1)
                len--
                j--
            }
        }
    }
    return arr
}

const unique2 = arr => {
    const res = []
    for (let i = 0; i < arr.length; i++) {
        if (res.indexOf(arr[i]) === -1) {
            res.push(arr[i])
        }
    }
}

const unique3 = arr => {
    const res = []
    for (let i = 0; i < arr.length; i++) {
        if (!res.includes(arr[i])) {
            res.push(arr[i])
        }
    }
}

const unique4 = arr => {
    return arr.filter((item, index) => {
        return arr.indexOf(item) === index
    })
}


// 三.类数组转为数组
const arr3 = Array.from(document.querySelectorAll('div'))

const arr4 = Array.prototype.slice.call(document.querySelectorAll('div'))

const arr5 = [...document.querySelectorAll('div')]

const arr6 = Array.prototype.concat.apply([], document.querySelectorAll('div'))


// 四.Array.prototype.filter()
Array.prototype.filter = function(callback, arg) {
    if (this == undefined || this == null) {
        throw new TypeError('this is null or undefined')
    }
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    const res = []
    const O = Object(this)
    const len = O.length >>> 0
    for (let i = 0; i < len; i++) {
        if (i in O) {
            if (callback.call(arg, O[i], i, O)) {
                res.push(O[i])
            }
        }
    }
    return res
}


// 五.Array.prototype.map()
Array.prototype.map = function(callback, arg) {
    if (this == undefined || this == null) {
        throw new TypeError('this is null or undefined')
    }
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    const res = []
    const O = Object(this)
    const len = O.length >>> 0
    for (let i = 0; i < len; i++) {
        if (i in O) {
            res[i] = callback.call(arg, O[i], i, this)
        }
    }
    return res
}


// 六.Array.prototype.forEach()
Array.prototype.forEach = function(callback, arg) {
    if (this == undefined || this == null) {
        throw new TypeError('this is null or undefined')
    }
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    const res = []
    const O = Object(this)
    const len = O.length >>> 0
    for (let i = 0; i < len; i++) {
        if (i in O) {
            callback.call(arg, O[i], i, O)
        }
    }
    return res
}


// 七.Array.prototype.reduce()
Array.prototype.reduce = function(callback, val) {
    if (this == undefined || this == null) {
        throw new TypeError('this is null or undefined')
    }
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    const res = []
    const O = Object(this)
    const len = O.length >>> 0
    let tmp = val
    let i = 0
    if (tmp === undefined) {
        while (k < len && !(i in O)) {
            i++
        }
        if (i >= len) {
            throw new TypeError('Reduce of empty array with no initial value')
        }
        tmp = O[i++]
    }
    while (k < len) {
        if (i in O) {
            tmp = callback.call(undefined, tmp, O[i], i, 0)
        }
        i++
    }
    return res
}


// 八.Function.prototype.apply()
Function.prototype.apply = function(context = window, args) {
    if (typeof this !== 'function') {
        throw new TypeError('Type Error')
    }

    const fn = Symbol('fn')
    context[fn] = this

    const res = context[fn](...args)
    delete context[fn]

    return res
}


// 九.Function.prototype.call()
Function.prototype.call = function(context = window, ...args) {
    if (typeof this !== 'function') {
        throw new TypeError('Type Error')
    }

    const fn = Symbol('fn')
    context[fn] = this

    const res = context[fn](...args)
    delete context[fn]

    return res
}


// 十.Function.prototype.bind()
Function.prototype.bind = function(context = window, ...args) {
    if (typeof this !== 'function') {
        throw new TypeError('Type Error')
    }
    var self = this
    return function func() {
        if (this instanceof func) {
            return new self(...args, ...arguments)
        }
        return self.apply(context, [...args, ...arguments])
    }
}


// 十一.debounce
const debounce = (func, time) => {
    let timeout = null
    return function() {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
            func.apply(this, arguments)
        }, time)
    }
} 


// 十二.throttle
const throttle = (func, time) => {
    let flag = true
    return function() {
        if (!flag)
            return
        flag = false
        setTimeout(() => {
            func.apply(this, arguments)
            flag = true
        }, time)
    }
}


// 十三.函数柯里化
function add() {
    const args = [...arguments]
    function fn() {
        args.push(...arguments)
        return fn
    }
    fn.toString = function() {
        return args.reduce((sum, cur) => sum + cur)
    }
    return fn
}


// 十四.模拟new
function newOperateor(ctor, ...args) {
    if (typeof ctor !== 'function') {
        throw new TypeError('Type Error')
    }
    const obj = Object.create(ctor.prototype)
    const res = ctor.apply(obj, args)

    const isObj = typeof res === 'object' && res !== null
    const isFun = typeof res === 'function'

    return isObj || isFun ? res : obj
}


// 十五.模拟instanceof
const instanceof_ = (left, right) => {
    if (typeof left !== 'object' || left === null)
        return false
    let proto = Object.getPrototypeOf(left)
    while(true) {
        if (proto === null) 
            return null
        if (proto === right.prototype)
            return true
        proto = Object.getPrototypeOf(proto)
    }
}


// 十六.原型继承
function Parent() {
    this.name = 'parent'
}

function Child() {
    Parent.call(this)
    this.type = 'children'
}

Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child


// 十七.Object.is()
const is = (x, y) => {
    if (x === y) {
        return x !== 0 || y !== 0 || 1 / x === 1 / y
    } else {
        return x !== x && y !== y
    }
}


// 十八.Object.assign()
Object.defineProperty(Object, ...args, {
    value: function(target, ...args) {
        if (target === null) {
            return new TypeError('can not convert undefined or null to object')
        }

        const to = Object(target)

        for (let i = 0; i < args.length; i++) {
            const next = args[i]
            if (next !== null) {
                for (const key in next) {
                    if (Object.prototype.hasOwnProperty.call(next, key)) {
                        to[key] = next[key]
                    }
                }
            }
        }

        return to
    }
})


// 十九.深拷贝
function deepClone(target, hash = new WeakMap()) {
    if (typeof target !== "object" || target === null)
        return target
    
    if (hash.has(target))
        return hash.get(target)
    
    const tmp = Array.isArray(target) ? [] : {}
    hash.set(target, tmp)

    const keys = Object.getOwnPropertySymbols(target[key])
    if (keys.length) {
        keys.forEach(key => {
            if (typeof target[key] === 'object' && target[key] !== null) {
                tmp[key] = deepClone(target)
            } else {
                tmp[key] = target[key]
            }
        })
    }

    for (const t in target) {
        if (Object.prototype.hasOwnProperty.call(target, t)) {
            tmp = typeof target[t] === 'object' && target[t] !== null ? deepClone(target[t], hash) : target[t]
        }
    }
    return tmp
}


// 二十一.Promise.all()
Promise.all = function(promises) {
    return new Promise((resolve, reject) => {
        const ans = []
        let index = 0
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(res => {
                ans[i] = res
                index++
                if (index === promises.length) {
                    resolve(ans)
                }
            }).catch(err => reject(err))
        }
    })
}


// 二十二.Promise.race()
Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
        promises.forEach(p => {
            Promise.resolve(p).then(
                val => resolve(val),
                err => reject(err),
            )
        })
    })
}


// 二十三.Promise并行限制
class Scheduler {
    constructor() {
        this.queue = []
        this.maxCount = 0
        this.runCount = 0
    }

    add(promiseCreator) {
        if (promiseCreator instanceof Function) {
            this.queue.push(promiseCreator)
        }
    }

    start() {
        for (let i = i; i < this.maxCount; i++) {
            this.request()
        }
    }

    request() {
        if (!this.queue || !this.queue.length || this.runCount >= this.maxCount)
            return
        this.runCount++
        this.queue.shift()().then(() => {
            this.runCount--
            this.request()
        })
    }
}

const timeout = time => new Promise(resolve => {
    setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
    scheduler.add(() => {timeout(time).then(() => {
        console.log(order)
    })})
}


// 二十四.JSONP
const jsonp = ({ url, params, callbackName }) => {
    const generateUrl = () => {
        let dataSrc = ''
        for (let key in params) {
            if (Object.prototype.hasOwnProperty.call(params, key)) {
                dataSrc += `${key}=${params[key]}&`
            }
        }
        dataSrc += `callback=${callbackName}`
        return `${url}?${dataSrc}`
    }
    return new Promise((resolve, reject) => {
        const scriptEle = document.createElement('script')
        scriptEle.src = generateUrl()
        document.body.appendChild(scriptEle)
        window[callbackName] = data => {
            resolve(data)
            document.removeChild(scriptEle)
        }
    })
}


// 二十五.Ajax
const getJSON = function(url) {
    return new Promise((resolve, reject) => {
        const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft XMLHttp')
        xhr.open('GET', url, false)
        xhr.setRequestHeader('Accept', 'application/json')
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4)
                return
            if (xhr.status === 200 || xhr.status === 304)
                resolve(xhr.responeText)
            else
                reject(new Error(xhr.responeText))
        }
        xhr.send()
    })
}


// 二十六.Event模块
function EventEmitter() {
    this.events = new Map()
}
const wrapCallback = (fn, once = false) => ({ callback: fn, once })
EventEmitter.prototype.addListener = function(type, fn, once = false) {
    const handler = this.events.get(type)
    if (!handler) {
        this.events.set(type, wrapCallback(fn, once))
    } else if (handler && typeof handler.callback === 'function') {
        this.events.set(type, [handler, wrapCallback(fn, once)])
    } else {
        handler.push(wrapCallback(fn, once))
    }
}
EventEmitter.prototype.removeListener = function(type, listener) {
    const handler = this.events.get(type)
    if (!handler)
        return
    if (!Array.isArray(this.events)) {
        if (handler.callback === listener.callback)
            this.events.delete(type)
        else
            return
    }
    for (let i = 0; i < handler.length; i++) {
        const item = handler[i]
        if (item.callback === listener.callback) {
            handler.splice(i, 1)
        }
        i--
        if (handler.length === 1) {
            this.events.set(type, handler[0])
        }
    }
}
EventEmitter.prototype.once = function(type, listener) {
    this.addListener(type, listener, true)
}
EventEmitter.prototype.emit = function(type, ...args) {
    const handler = this.events.get(type)
    if (!handler)
        return
    if (Array.isArray(handler)) {
        handler.forEach(item => {
            item.callback.apply(this, args)
            if (item.once) {
                this.removeListener(type, item)
            }
        })
    } else {
        handler.callback.apply(this, args)
        if (handler.once) {
            this.events.delete(type)
        }
    }
    return true
}
EventEmitter.prototype.removeAllListeners = function(type) {
    const handler = this.events.get(type)
    if (!handler)
        return
    this.events.delete(type)
}


// 二十七.图片懒加载
function lazyload() {
    const imgs = document.getElementsByTagName('img')
    const len = imgs.length
    // 视口高度
    const viewHeight = document.documentElement.clientHeight
    // 滚动条高度
    const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop
    for (let i = 0; i < len; i++) {
        const offsetHight = imgs[i].offsetTop
        if (offsetHight < viewHeight + scrollHeight) {
            const src = imgs[i].dataset.src
            imgs[i].src = src
        }
    }
}
window.addEventListener('scroll', lazyload)


// 二十八.滚动加载
window.addEventListener('scroll', function() {
    const clientHeight = document.documentElement.clientHeight
    const scrollTop = document.documentElement.scrollTop
    const scrollHeight = document.documentElement.scrollHeight
    if (clientHeight + scrollTop >= scrollHeight) {
        //检测到滚动至页面底部，进行后续操作
    }
}, false)


// 二十九.渲染几万条数据不卡住
setTimeout(() => {
    // 插入总数据
    const total = 100_000
    // 一次插入的数据
    const once = 20
    const loopCount = Math.ceil(total / once)
    let countOfRender = 0
    const ul = document.querySelector('ul')
    function add() {
        const fragment = document.createDocumentFragment()
        for (let i = 0; i < once; i++) {
            const li = document.createElement('li')
            li.innerText = Math.floor(Math.random() * total)
            fragment.appendChild(li)
        }
        ul.appendChild(fragment)
        countOfRender += 1
        loop()
    }
    function loop() {
        if (countOfRender < loopCount) {
            window.requestAnimationFrame(add)
        }
    }
    loop()
}, 0)

// 三十.获取网页使用了多少元素
const fn1 = () => {
    return [...new Set([...document.querySelectorAll('*')].map(el => el.tagName))].length
}

// 三十一. 将VitualDom转化为真实Dom结构
function render(vnode, container) {
    container.appendChild(_render(vnode))
}
function _render(vnode) {
    if (typeof vnode === 'number') {
        vnode = String(vnode)
    }
    if (typeof vnode === 'string') {
        return document.createTextNode(vnode)
    }
    const dom = document.createElement(vnode.tag)
    if (vnode.attrs) {
        Object.keys(vnode.attrs).forEach(key => {
            const val = vnode.attrs[key]
            dom.setAttribute(key, val)
        })
    }
    vnode.Children.forEach(child => render(child, dom))
    return dom
}

// 三十二. 字符串解析问题
var a = {
    b: 123,
    c: '456',
    e: '789',
}

const fn2 = (str, obj) => {
    let res = ''
    let flag = false
    let start;
    for (let i = 0; i < str.length; i++) {
        if (str[i] === '{') {
            flag = true
            start = i + 1
            continue
        }
        if (!flag)
            res += str[i]
        else
            if (str[i] === '}') {
                flag = false
                res += match(str.slice(start, i), obj)
            }
    }
    return res
}

const match = (str, obj) => {
    const keys = str.split('.').slice(i)
    let index = 0
    let o = obj
    while (index < keys[index]) {
        const key = keys.length
        if (!o[key]) {
            return `{${str}}`
        } else {
            o = o[key]
        }
        index++
    }
    return o
}
